<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.3.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"chuangges.gitee.io","root":"/","scheme":"Pisces","version":"7.8.0","exturl":false,"sidebar":{"position":"right","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"mac"},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="技术网站、架构模式、浏览器机制、JS 运行机制、项目开发规范、网站安全规范">
<meta property="og:type" content="article">
<meta property="og:title" content="web 开发须知">
<meta property="og:url" content="https://chuangges.gitee.io/2019/02/17/2-1-web%E5%BC%80%E5%8F%91%E9%A1%BB%E7%9F%A5/index.html">
<meta property="og:site_name" content="闯哥要去流浪了">
<meta property="og:description" content="技术网站、架构模式、浏览器机制、JS 运行机制、项目开发规范、网站安全规范">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://chuangges.gitee.io/images/web/browserCom.png">
<meta property="og:image" content="https://chuangges.gitee.io/images/web/js-run.png">
<meta property="og:image" content="https://chuangges.gitee.io/images/mobile/optimization.png">
<meta property="article:published_time" content="2019-02-17T07:41:48.000Z">
<meta property="article:modified_time" content="2023-09-25T09:25:01.899Z">
<meta property="article:author" content="chuangges">
<meta property="article:tag" content="前端技术栈">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://chuangges.gitee.io/images/web/browserCom.png">

<link rel="canonical" href="https://chuangges.gitee.io/2019/02/17/2-1-web%E5%BC%80%E5%8F%91%E9%A1%BB%E7%9F%A5/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>web 开发须知 | 闯哥要去流浪了</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/atom.xml" title="闯哥要去流浪了" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">闯哥要去流浪了</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">愿你岁月可回首，且以深情共白头</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://chuangges.gitee.io/2019/02/17/2-1-web%E5%BC%80%E5%8F%91%E9%A1%BB%E7%9F%A5/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/chuang.png">
      <meta itemprop="name" content="chuangges">
      <meta itemprop="description" content="chuangge's blog.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="闯哥要去流浪了">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          web 开发须知
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2019-02-17 15:41:48" itemprop="dateCreated datePublished" datetime="2019-02-17T15:41:48+08:00">2019-02-17</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2023-09-25 17:25:01" itemprop="dateModified" datetime="2023-09-25T17:25:01+08:00">2023-09-25</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%89%8D%E7%AB%AF%E6%8A%80%E6%9C%AF%E6%A0%88/" itemprop="url" rel="index"><span itemprop="name">前端技术栈</span></a>
                </span>
            </span>

          <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="far fa-file-word"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>25k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="far fa-clock"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>23 分钟</span>
            </span>
            <div class="post-description">技术网站、架构模式、浏览器机制、JS 运行机制、项目开发规范、网站安全规范</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h1 id="一、技术网站"><a href="#一、技术网站" class="headerlink" title="一、技术网站"></a>一、技术网站</h1><blockquote>
<p>web 开发就是呈现用户可理解的界面并响应用户操作。wget 扒取网站源码：<code>brew install wget、wget -c -r -np -nc -L -k -p http://xxx.com</code>。</p>
</blockquote>
<ul>
<li>技术学习<ul>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://www.nowcoder.com/">牛客网</a>：面试题库</li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://www.docschina.org/">印记中文</a>：中文教程  </li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="http://tool.lu/">在线工具</a>：代码处理工具合集</li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://gitee.com/">码云</a>：代码托管中文网站</li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://github.com/">Github</a>：代码托管全球网站  </li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://stackoverflow.com/">Stack Overflow</a>：技术问答社区</li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="http://tool.chinaz.com/tools/openweb.aspx/">站长工具</a>：代码整理和测试</li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://animista.net/">CSS 动画</a>：CSS 动画  </li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://carbon.now.sh/">Carbon</a>：图片生成器</li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://www.favicon-generator.org/">Faviconer</a>：图标生成器 </li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://www.processon.com/">Slides</a>：WebPPT 编辑器</li>
<li><a target="_blank" rel="external nofollow noopener noreferrer" href="https://learn-anything.xyz/">Learn Anything</a>：思维导图</li>
</ul>
</li>
<li>网站搭建<ul>
<li><strong>WordPress</strong>：开源的建站系统，从个人博客到企业电商平台都可以轻松实现。</li>
<li><strong>Docsify、Dumi</strong>：简单的文档站点生成器，区别是 Dumi 专为组件开发场景而生。</li>
<li><strong>Img Cook</strong>：阿里团队开发的页面生成器，通过人工智能根据设计稿一键生成代码。</li>
<li><strong>Ant Design Landing</strong>：蚂蚁开发的网站首页生成器，方便我们快速上线网站首页。</li>
<li><strong>腾讯兔小巢</strong>：腾讯提供的免费专业的产品反馈平台，可以为我们的产品一键生成一个专属的反馈论坛，从而省去了自己搭建社区的麻烦。</li>
<li><strong>百度统计</strong>：超级好用的中文网站分析平台，支持网站、APP、小程序、线下零售等各种场景，轻松满足我们网站的数据自动采集、统计和分析需求。</li>
<li><strong>One Nav</strong>：基于开源的 Web Stack 开发，提供了多引擎搜索、新闻、热榜等功能。</li>
<li><strong>Discuz</strong>：兼容 PC 端、移动端、微信生态的开源社区软件，无需开发即可拥有包括文章、视频、评论、点赞、关注、知识付费等的个人社区。</li>
<li><strong>编程主页</strong>：鱼皮开发的程序员精简主页，感兴趣的同学可以把代码拷到本地修改。</li>
<li><strong>后台管理</strong>：vue-element-admin 、react-admin 、go-admin 等都是开箱即用的模版，能省去重复搭建管理后台的麻烦。</li>
</ul>
</li>
</ul>
<h1 id="二、架构模式"><a href="#二、架构模式" class="headerlink" title="二、架构模式"></a>二、架构模式</h1><blockquote>
<p>客户端是上网时的手机电脑等机器，服务器则是可保持长时间运行并可随时访问的性能强大的机器，项目开发前一定要考虑到两者的环境差异。对于客户端渲染模式存在的<strong>首页白屏</strong>问题，可采用方式有分拆打包 (路由库)、交互优化 (加载动画)、部分同构 (菜单和页面骨架)。</p>
</blockquote>
<h2 id="CSR-客户端渲染"><a href="#CSR-客户端渲染" class="headerlink" title="CSR 客户端渲染"></a>CSR 客户端渲染</h2><ul>
<li><strong>实现</strong>：浏览器访问 React、Vue 构建的单页面应用时服务器只返回一个 html 空白模版，浏览器加载外链 js 并解析生成 dom 元素后才能渲染出完整页面，页面加载的过程为白屏时间。</li>
<li><strong>优势</strong>：低耦合的前后端分离设计、专注于接口的服务器压力小、支持局部刷新的按需加载。</li>
<li><strong>问题</strong>：不利于 SEO (搜索引擎不执行 JS 而没有数据)、白屏时间长 (客户端加载所有资源)。</li>
</ul>
<h2 id="Next-x2F-Nuxt-同构渲染"><a href="#Next-x2F-Nuxt-同构渲染" class="headerlink" title="Next&#x2F;Nuxt 同构渲染"></a>Next&#x2F;Nuxt 同构渲染</h2><ul>
<li><strong>实现</strong>：服务器和客户端都可以运行的一套 JS 代码 (项目构建时拆出两套)，浏览器将服务端 NodeJS 环境拼接出包含内容的 html 字符串直接插入到指定节点后注入样式和事件即可。</li>
<li><strong>优点</strong>：提高首屏渲染性能而避免白屏、有利于 SEO 搜索、前后端共用代码而节省开发时间。</li>
<li><strong>缺点</strong>：内存溢出和异步操作等性能、两端环境差异、复杂类型无法转义为字符串发给客户端。</li>
</ul>
<h2 id="SSR-服务端渲染"><a href="#SSR-服务端渲染" class="headerlink" title="SSR 服务端渲染"></a>SSR 服务端渲染</h2><ul>
<li><strong>实现原理</strong>：ASP、JAVA 等在服务端拼接出包含完整内容的 html，浏览器收到后直接渲染。</li>
<li><strong>优点</strong>：有利于 SEO 关键字的搜索匹配、客户端的电量消耗小、页面的渲染速度较快。</li>
<li><strong>缺点</strong>：较大的服务器负载 (CPU 和内存资源)、页面交互和跳转会导致整个页面刷新。</li>
</ul>
<h2 id="SSG-静态预渲染"><a href="#SSG-静态预渲染" class="headerlink" title="SSG 静态预渲染"></a>SSG 静态预渲染</h2><ul>
<li><strong>实现</strong>：VuePress、VitePress、Gatsby 等在编译构建时预先渲染出包含内容的静态 html，然后将 html css 等静态资源发布到 CDN 节点，适合公司官网等变动频率不高的静态资源。</li>
<li><strong>优点</strong>：服务器压力小 (不需要实时渲染页面和响应客户端数据)、有利于 SEO 搜索。</li>
<li><strong>缺点</strong>：只支持静态内容 (数据更新时重新生成页面)、更新时需要重新渲染整个应用。</li>
</ul>
<h2 id="ISR-增量静态渲染"><a href="#ISR-增量静态渲染" class="headerlink" title="ISR 增量静态渲染"></a>ISR 增量静态渲染</h2><ul>
<li><strong>实现</strong>：初次构建静态网站时生成所有页面并缓存，服务端收到内容更新时对当前页面做失效验证和增量构建，下次请求到达时会返回已生成的新页面 (如果没有生成则会返回旧页面)。</li>
<li><strong>优点</strong>：不需要在构建期间预渲染所有页面、内容更新时重新生成页面而无需重建整个应用。</li>
<li><strong>缺点</strong>：部分页面没有预渲染、已渲染的页面可能已过期而需要用户刷新 (不适用于电商场景)。</li>
</ul>
<h1 id="三、浏览器机制"><a href="#三、浏览器机制" class="headerlink" title="三、浏览器机制"></a>三、浏览器机制</h1><blockquote>
<p>浏览器是 Html 解析和展示页面的工具，主要功能是将用户选择的 web 资源呈现出来。它需要从服务器请求资源并将其显示在浏览器窗口中，资源格式一般有 HTML、PDF、image 等。渲染引擎是用户直接相关并呈现用户所需页面的组成部分。</p>
</blockquote>
<h2 id="抽象分层结构"><a href="#抽象分层结构" class="headerlink" title="抽象分层结构"></a>抽象分层结构</h2><ul>
<li><strong>用户界面</strong>：用于和用户进行交互的功能组件，如地址栏、返回、前进按钮等。</li>
<li><strong>浏览器引擎</strong>：用于查询和操作渲染引擎的界面。</li>
<li><strong>渲染引擎</strong>：负责显示请求的内容，如果请求 HTML 则它解析 HTML、CSS 并显示。</li>
<li><strong>网络</strong>：负责处理网络相关的事务，如 HTTP 请求等。</li>
<li><strong>UI 后端</strong>：负责绘制提示框等浏览器组件，底层使用操作系统的用户接口。</li>
<li><strong>JS 解释器</strong>：负责解析和执行 JavaScript 代码。</li>
<li><strong>数据存储</strong>：负责持久存储 cookie 和缓存等应用数据。</li>
</ul>
  <div align="center">
    <img src="" data-original="/images/web/browserCom.png" alt="浏览器结构" width="80%">
  </div>


<h2 id="多进程机制"><a href="#多进程机制" class="headerlink" title="多进程机制"></a>多进程机制</h2><blockquote>
<p>浏览器是多进程的，每打开一个 Tab 页就相当于创建了一个独立的进程，单进程的话则会存在一个页面崩溃而影响整个浏览器。浏览器由于系统给它的进程分配了内存等资源而可以运行，当前进程里并发运行的线程会共享有限的内存地址空间，但无法获取其它进程的数据。</p>
</blockquote>
<ul>
<li>进程和线程<ul>
<li><strong>CPU</strong>：工厂，是执行计算任务的计算机核心。进程与线程都是描述 CPU 工作时间段。</li>
<li><strong>进程</strong>：工厂车间，是资源分配的最小单位，拥有独立的地址空间，独立执行一个任务。</li>
<li><strong>线程</strong>：车间工人，是程序执行的 CPU 最小单位，需要协同其它线程才能完成一个任务。</li>
<li><strong>并行</strong>：通过多进程、多线程等实现多个任务在同一时刻同时执行，用于 CPU 密集任务。</li>
<li><strong>并发</strong>：通过线程、协程等实现多个任务在同一时间段内交替执行，用于 I&#x2F;O 密集任务。</li>
</ul>
</li>
<li>进程间通信<ul>
<li><strong>管道</strong>：进程以先进先出的方式从缓冲区存取数据，管道两端的进程分别写入和读取。使用简单但缺点是内核的缓冲区是有限的，并且存在阻塞机制而不适合频繁通信的情况。</li>
<li><strong>信号</strong>：当前进程未执行时存于内核，通过进程和内核直接交互而控制进程终止退出等。</li>
<li><strong>消息队列</strong>：解决了管道只能单向传输和存在阻塞机制的问题，但数据较大时拷贝效率低。注意匿名管道和具名管道分别存于内存文件和系统磁盘，消息队列则存于内核中。</li>
<li><strong>共享内存</strong>：多个进程共享一块地址空间而提高效率，地址空间并不是真实的物理内存。</li>
<li><strong>信号量</strong>：为了解决多线程共享内存时不会同时修改，本质是进程访问内存的计数器，思考如果存在多个线程同时修改 DOM，那么页面中的 DOM 究竟是处于什么状态。</li>
<li><strong>Socket</strong>：以上方式可实现同一个计算机的进程通信，Socket 套接字则是用于不同计算机之间的进程通信。Socket 是应用层与传输层之间的桥梁。</li>
</ul>
</li>
<li>模式类型<ul>
<li><strong>单线程</strong>：所有任务在主线程中排队执行，有错误时无法执行后续也就是阻塞。</li>
<li><strong>多线程</strong>：共享当前进程的地址空间，一个线程崩溃了可能导致整个进程崩溃。</li>
<li><strong>多进程</strong>：支持同时执行多任务，拥有独立的地址空间并且互不影响，但消耗更多资源。</li>
<li><strong>协调机制</strong>：防止进程之间、线程之间产生冲突，允许进程之间、线程之间共享资源。</li>
</ul>
</li>
<li>多进程的优势<ul>
<li>多进程充分利用多核优势。</li>
<li>避免单个页面或插件崩溃而影响整个浏览器。</li>
<li>方便使用沙盒模型隔离插件等进程，提高浏览器稳定性。</li>
</ul>
</li>
<li>内部进程<ul>
<li><strong>Browser 进程</strong>：浏览器的主进程 (负责协调和主控)，只有一个。主要功能为绘制渲染进程的 Bitmap、浏览器界面的显示和操作、管理页面进程和网络资源。</li>
<li><strong>Render 进程</strong>：浏览器内核，内部是多线程的，默认每个 Tab 页面对应一个进程并互不影响。主要负责页面渲染、脚本执行、事件处理等。</li>
<li><strong>第三方插件进程</strong>：每种类型的插件对应一个进程。</li>
<li><strong>GPU 进程</strong>：用于 3D 绘制等，最多一个。</li>
</ul>
</li>
<li><strong>内核的多线程</strong>：为了防止出现同时修改元素属性和渲染界面时可能导致的渲染问题，浏览器设置 GUI 渲染线程和 JS 引擎线程为互斥关系，GUI 线程会在 JS 引擎执行时被挂起并保存到队列中等待执行，所以 JS 可能阻塞渲染。<ul>
<li><strong>GUI 渲染线程</strong>：负责渲染浏览器界面，解析 HTML、CSS 并构建 DOM、RenderObject 树等。当界面需要重绘或某种操作引发回流时就会执行。</li>
<li><strong>JS 引擎线程</strong>：即 JS 内核，负责解析执行 JS 脚本代码。一直通过事件循环机制查看并执行任务队列中的任务。</li>
<li><strong>事件触发线程</strong>：浏览器中用来控制事件循环的线程，该线程会将符合触发条件的事件添加到任务队列的队尾等待处理。</li>
<li><strong>定时触发器线程</strong>：setInterval、setTimeout 所在线程，用来在计时完毕后添加到事件队列中等待 JS 引擎空闲时执行。</li>
<li><strong>异步 http 请求线程</strong>：XMLHttpRequest 连接后是通过浏览器新开一个线程请求。当检测到状态变更时，如果设置了回调函数则异步线程就产生状态变更事件，将回调函数再放入事件队列中等待 Js 引擎执行。</li>
</ul>
</li>
<li><strong>内核和 Browser 的通信</strong><ul>
<li>1、Browser 进程收到用户请求，通过网络下载或本地加载获取页面内容。</li>
<li>2、Render 进程的接口收到 Browser 进程传递的请求后交给内部的 GUI 渲染线程执行并返回结果。渲染过程中可能存在：向 Browser 进程获取资源、需要 GPU 进程帮助渲染、JS 线程操作 DOM 导致回流并重绘。</li>
<li>3、Browser 进程接收到结果并将结果绘制出来。</li>
</ul>
</li>
</ul>
<h2 id="渲染机制"><a href="#渲染机制" class="headerlink" title="渲染机制"></a>渲染机制</h2><blockquote>
<p>即渲染引擎工作流，为了更好的用户体验，渲染引擎会尽可能早的将内容呈现到屏幕上，并不会等到所有 html 解析完成之后再去构建和布局 render tree。它是解析完一部分内容就显示一部分内容，同时可能还在网络下载其余内容。相关事件：DOM 树构建完成时触发 DOMContentLoaded 事件，页面加载完成时触发 load 事件。</p>
</blockquote>
<ol>
<li><strong>浏览器解析 url</strong><br>1. 用户输入 URL，浏览器解析后获取主机名和端口号。<br>2. 通过 DNS 查询将主机名转换成服务器 IP 的地址。<br>3. 浏览器建立一条与目标服务器的 TCP 连接（三次握手）。<br>4. 浏览器向服务器发送一条 HTTP 请求报文并获取服务器响应。<br>5. 浏览器关闭连接并重复请求至资源全部加载后，解析文档内容并渲染到页面。<br>6. 通过四次挥手关闭连接，解析结束。</li>
<li><strong>浏览器渲染</strong><br>1. 浏览器接收到 index.html 后在内存中开辟一个主任务执行栈，然后为 html 开辟一个环境并分配一个线程来执行代码，执行语句时入栈并执行完出栈，执行到 link 等加载资源的代码时就开辟新线程发送请求，主线程继续往下执行并将请求结果放到任务队列按顺序执行。<br>2. 解析 HTML 标记并构建 DOM Tree。<br>3. 解析 CSS 标记并构建 CSSOM Tree。<br>4. 根据 DOM、CSSOM 构建渲染树 Render Tree，节点是包含颜色等属性的渲染对象。<br>5. 绘制：根据渲染树和浏览器视口大小获取页面中每个元素的大小位置，然后 GPU 就会根据渲染树和元素的具体样式将所有元素渲染到页面。<br>6. 重流重绘：元素样式更新时引起当前元素的重绘，元素的大小位置更新时引起整个页面的布局重流，它们都会重新生成渲染树，但是重绘不一定引发回流，重流一定发生重绘。</li>
<li><strong>资源下载问题</strong><br>* 图片下载不会产生阻塞。<br>* css 下载时会阻塞渲染但带有 media 属性除外：css 由单独的下载线程异步下载，加载时不会阻塞 DOM 树解析但会阻塞渲染树渲染。<br>* script 标签出现时，DOM 构建停止直到 js 脚本下载并执行完毕。此时浏览器一般会下载其它资源但不会解析，js 中存在操作 CSSOM 时还会先确保 CSSOM 已被下载并构建。</li>
</ol>
<h2 id="优化方案"><a href="#优化方案" class="headerlink" title="优化方案"></a>优化方案</h2><ul>
<li><strong>CSS</strong><ul>
<li>CSS 硬件加速。</li>
<li>避免使用 table 布局。</li>
<li>使用 transform 代替 top。</li>
<li>尽可能在 DOM 树的最末端改变 class。</li>
<li>避免使用 CSS 表达式，可能会引发回流。</li>
<li>避免多层内联样式，CSS 选择符从右往左匹配查找，减少节点层级。</li>
<li>使用 visibility 替换 display: none，前者引起重绘，后者引发回流。</li>
<li>动画效果应用到 position: absolute&#x2F;fixed 元素，不影响其他布局。</li>
</ul>
</li>
<li><strong>JS</strong><ul>
<li>避免频繁操作样式，最好修改 class。</li>
<li>避免频繁操作 DOM，合并多次修改为一次。</li>
<li>避免频繁读取会引发回流&#x2F;重绘的属性，将结果缓存。</li>
<li>对具有复杂动画的元素使用绝对定位，使它脱离文档流。</li>
</ul>
</li>
<li><strong>减少重流</strong><ul>
<li>虚拟 DOM 机制</li>
<li>读写分离机制</li>
<li>文件压缩合并</li>
<li>图片懒加载</li>
<li>音视频用流文件</li>
</ul>
</li>
</ul>
<h1 id="四、JS-运行机制"><a href="#四、JS-运行机制" class="headerlink" title="四、JS 运行机制"></a>四、JS 运行机制</h1><h2 id="JS-引擎"><a href="#JS-引擎" class="headerlink" title="JS 引擎"></a>JS 引擎</h2><ul>
<li><strong>JS 引擎</strong>：能够读懂 JS 代码并能准确运行出结果的一段程序。</li>
<li><strong>V8 引擎</strong>：运行前将 JS 编译为本地的机器码并执行，主要用来提高性能。</li>
<li>对于 Java、C++、C 等静态语言处理上述事情的叫 <strong>编译器</strong>，而对于 JS 这样的动态语言则叫做 <strong>解释器</strong>。区别：编译器是将源代码编译为另外一种代码 (比如机器码或字节码)，解释器则是直接解析并将代码运行结果输出。</li>
</ul>
<h2 id="JS-线程"><a href="#JS-线程" class="headerlink" title="JS 线程"></a>JS 线程</h2><ul>
<li><strong>JS 单线程</strong>：同一时间只能执行一个任务而其他任务需要排队等待执行，这是因为 JS 用来和用户交互，而多线程同时操作可能会带来复杂的同步问题。浏览器中负责解释和执行代码的 JS 引擎线程称为<strong>主线程</strong>，处理耗时异步任务的其它线程称为<strong>工作线程</strong>。</li>
<li><strong>WebWorker</strong>：只属于某个页面而不会和其他页面的 Render 进程共享。Chrome Render 进程中可新建一个 Worker 线程。它是 HTML5 为了利用多核 CPU 的计算功能而允许 JS 脚本创建多个线程来处理耗时操作的新标准，但是 Worker 是 JS 引擎向浏览器申请开的子线程，它完全受主线程控制并且不能操作 DOM，所以并没有改变JS单线程的本质。</li>
<li><strong>SharedWorker</strong>：浏览器的所有页面共享。Chrome 可新建一个 SharedWorker 进程，但相同 JS 不论创建多少次也只会存在一个。</li>
</ul>
<h2 id="JS-任务"><a href="#JS-任务" class="headerlink" title="JS 任务"></a>JS 任务</h2><blockquote>
<p>所有同步任务都是在主线程上执行而形成一个执行栈，主线程解析异步操作后会将异步任务放到一个具有先进先出特点的任务队列，主线程将同步任务执行完之后会循环读取任务队列中的事件并放到执行栈执行 (这个过程叫事件循环)。</p>
</blockquote>
<ul>
<li><strong>同步任务</strong>：主线程的执行栈中排队执行的任务，类似银行排队办业务。</li>
<li><strong>异步任务</strong>：添加到任务列队中延迟执行的任务，类似餐厅点餐后等待。<ul>
<li><strong>宏任务</strong>：延时执行队列和普通消息队列等队列中的任务，主线程通过事件循环每次从队列中取出一个任务并执行，一个宏任务执行完之后会重新渲染页面。比如 script、setTimeout、setInterval、I&#x2F;O、UI render。</li>
<li><strong>微任务</strong>：宏任务中需要异步执行的所有任务，在主函数执行结束后和当前宏任务结束前全部执行，执行微任务过程中产生的微任务不会推迟到下个宏任务中执行。比如 Promise、async&#x2F;await、Object.observe、MutationObserver、process.nextTick。</li>
</ul>
</li>
<li><strong>常见异步任务</strong><ul>
<li><strong>onclick 等事件的销毁函数</strong>：当事件触发时，回调函数会被立即添加到任务队列中。由浏览器内核的 DOM Binding 模块处理。</li>
<li><strong>setInterval 等计时器</strong>：当定时器时间到，就把该事件放到 任务队列中 等待处理。由浏览器内核的 timer 模块进行延时处理。</li>
<li><strong>Ajax 请求</strong>：在网络请求完成返回之后，将回调函数添加到任务队列中。由浏览器内核的 network 模块处理。</li>
</ul>
</li>
</ul>
<h2 id="异步机制"><a href="#异步机制" class="headerlink" title="异步机制"></a>异步机制</h2><ol>
<li>JS 引擎线程首先将所有操作分为同步任务、异步宏任务、异步微任务，然后根据顺序依次执行：<strong>先同步任务后异步任务队列、先微任务后宏任务</strong>。</li>
<li><strong>同步任务在主线程中会直接按顺序执行</strong>。异步任务则会被分发到任务队列，主线程空闲时读取队列任务，符合触发条件的会被分发到对应的工作线程执行并返回结果。</li>
<li>事件循环机制：主线程执行完所有同步任务之后空闲时，重复去任务队列中查看是否有可执行的异步任务，有则推入主进程的执行栈。<strong>事件循环由一个宏任务队列和多个微任务队列组成</strong>，一个宏任务执行完代表完成一次事件循环，微任务比宏任务有更高的优先级。<br>1. 执行一个宏任务（栈中没有就从事件队列中获取）。<br>2. 执行过程中如果遇到微任务，就将它添加到微任务的任务队列中。<br>3. 宏任务执行完毕后，立即执行当前微任务队列中的所有微任务（依次执行）。<br>4. 当前宏任务执行完毕，开始检查渲染，然后 GUI 线程接管渲染。<br>5. 渲染完毕后，JS 线程继续接管，开始下一个宏任务（从任务队列中获取）。</li>
</ol>
  <div align="center">
    <img src="" data-original="/images/web/js-run.png" alt="执行机制" width="100%">
  </div>


  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 输出结果：1、7、6、8、2、4、3、5、9、11、10、12</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;2&#x27;</span>);</span><br><span class="line">    process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;3&#x27;</span>);</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;4&#x27;</span>);</span><br><span class="line">        <span class="title function_">resolve</span>();</span><br><span class="line">    &#125;).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;5&#x27;</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)</span><br><span class="line">process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;6&#x27;</span>);</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;7&#x27;</span>);</span><br><span class="line">    <span class="title function_">resolve</span>();</span><br><span class="line">&#125;).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;8&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;9&#x27;</span>);</span><br><span class="line">    process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;10&#x27;</span>);</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;11&#x27;</span>);</span><br><span class="line">        <span class="title function_">resolve</span>();</span><br><span class="line">    &#125;).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;12&#x27;</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<ol>
<li><strong>第一轮事件循环</strong><br>* 整体 script 作为第一个宏任务进入主线程，执行 console.log 输出 1<br>* setTimeout，其回调函数被分发到宏任务队列，记为 setTimeout1<br>* process.nextTick()，其回调函数被分发到微任务队列，记为 process1<br>* Promise、new Promise 直接执行输出 7。then 被分发到微任务队列，记为 then1<br>* 第二个 setTimeout，其回调函数被分发到宏任务队列，记为 setTimeout2<br>* 执行两个微任务 process1、then1 输出 6、8<br>* 输出结果：1、7、6、8</li>
<li><strong>第二轮事件循环</strong><br>* 从宏任务 setTimeout1 开始，输出 2<br>* process.nextTick()，同样将其分发到微任务队列，记为 process2<br>* new Promise 立即执行输出 4，then 也分发到微任务队列，记为 then2<br>* 执行两个微任务 process2、then2 输出 3、5<br>* 输出结果：2、4、3、5</li>
<li><strong>第三轮事件循环</strong><br>* 此时只剩下 setTimeout2，执行输出 9<br>* process.nextTick() 分发到微任务队列，记为 process3<br>* new Promise 直接执行输出 11，then 分发到微任务队列，记为 then3<br>* 执行两个微任务 process3、then3 输出 10、12<br>* 输出结果：9、11、10、12</li>
</ol>
<h1 id="五、项目开发规范"><a href="#五、项目开发规范" class="headerlink" title="五、项目开发规范"></a>五、项目开发规范</h1><h2 id="项目目录"><a href="#项目目录" class="headerlink" title="项目目录"></a>项目目录</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1、</span></span><br><span class="line">src                  <span class="comment">// 项目目录</span></span><br><span class="line">├─config             <span class="comment">// 项目配置</span></span><br><span class="line">├─api                <span class="comment">// 数据请求 </span></span><br><span class="line">├─assets             <span class="comment">// 静态资源</span></span><br><span class="line">│  ├─css          </span><br><span class="line">│  └─images       </span><br><span class="line">├─components         <span class="comment">// 组件           </span></span><br><span class="line">├─layout             <span class="comment">// 布局</span></span><br><span class="line">├─routes             <span class="comment">// 路由 </span></span><br><span class="line">├─store              <span class="comment">// 状态         </span></span><br><span class="line">├─pages              <span class="comment">// 页面</span></span><br><span class="line">│  └─<span class="title class_">Home</span>              </span><br><span class="line">│  └─<span class="title class_">Kind</span>               </span><br><span class="line">├─utils              <span class="comment">// 工具方法</span></span><br><span class="line">└─main.<span class="property">ts</span>            <span class="comment">// 入口文件</span></span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 架构规范</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> Restful：面向资源，客户端操作基于通过服务器端资源对应的 URL。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> SOA：面向服务，将不同的功能组件封装为可复用服务，降低组件耦合。</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> Restful 特点：</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 资源；资源的标识，比如 html、jpg、json。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> URI：统一资源定位符，包括 URL、版本号、？&amp; 等修饰符。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 无状态：所有资源都可以通过 URI 去定位，而不与其他资源产生耦合。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 统一接口：数据元操作 CURD 对应 http get、post、put、delete。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> git 提交规范</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> husky: 可监听 githooks，在对应 hook 执行时做自定义处理。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">lint</span>-staged: 只检测暂存区文件代码，优化 eslint 检测速度。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">pre</span>-commit：githooks 之一， commit 前使用检测语法。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">commit</span>-msg：githooks 之一，commit 前检测备注信息。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> commitlint：pre-commit 阶段检测 commit 备注信息。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> commitizen：规范化提交工具，辅助填写 commit 信息。</span></span><br><span class="line"><span class="comment">**/</span> </span><br></pre></td></tr></table></figure>

<h2 id="公共方法"><a href="#公共方法" class="headerlink" title="公共方法"></a>公共方法</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/utils/storage.ts：类型自动推断、抹平平台差异、统一管理</span></span><br><span class="line"><span class="keyword">const</span> prefix = <span class="string">&#x27;xxx.&#x27;</span></span><br><span class="line">interface <span class="title class_">IStorage</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="attr">key</span>: string</span><br><span class="line">    <span class="attr">defaultValue</span>: T</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">LocalStorage</span>&lt;T&gt; implements <span class="title class_">IStorage</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="attr">key</span>: string</span><br><span class="line">    <span class="attr">defaultValue</span>: T</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">key, defaultValue</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">key</span> = prefix + key</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">defaultValue</span> = defaultValue</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">setItem</span>(<span class="params">value: T</span>) &#123;</span><br><span class="line">        <span class="variable language_">localStorage</span>.<span class="title function_">setItem</span>(<span class="variable language_">this</span>.<span class="property">key</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(value))</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">getItem</span>(): T &#123;</span><br><span class="line">        <span class="keyword">const</span> hasKey = <span class="variable language_">localStorage</span>[<span class="variable language_">this</span>.<span class="property">key</span>]</span><br><span class="line">        <span class="keyword">const</span> value = hasKey &amp;&amp; <span class="variable language_">localStorage</span>.<span class="title function_">getItem</span>(<span class="variable language_">this</span>.<span class="property">key</span>)</span><br><span class="line">        <span class="keyword">if</span> (value === <span class="literal">undefined</span>) <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">defaultValue</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> value &amp;&amp; value !== <span class="string">&#x27;null&#x27;</span> &amp;&amp; value !== <span class="string">&#x27;undefined&#x27;</span></span><br><span class="line">                ? (<span class="title class_">JSON</span>.<span class="title function_">parse</span>(value) <span class="keyword">as</span> T) : <span class="variable language_">this</span>.<span class="property">defaultValue</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">            <span class="keyword">return</span> value &amp;&amp; value !== <span class="string">&#x27;null&#x27;</span> &amp;&amp; value !== <span class="string">&#x27;undefined&#x27;</span></span><br><span class="line">                ? (value <span class="keyword">as</span> unknown <span class="keyword">as</span> T) : <span class="variable language_">this</span>.<span class="property">defaultValue</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">removeItem</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">localStorage</span>.<span class="title function_">removeItem</span>(<span class="variable language_">this</span>.<span class="property">key</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// src/common/storage.ts</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">LocalStorage</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@/utils/storage&#x27;</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> tokenStorage = <span class="keyword">new</span> <span class="title class_">LocalStorage</span>&lt;string&gt;(<span class="string">&#x27;token&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line"><span class="keyword">export</span> interface <span class="title class_">IUser</span> &#123;</span><br><span class="line">    name?: string</span><br><span class="line">    age?: num</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> userStorage = <span class="keyword">new</span> <span class="title class_">Storage</span>&lt;<span class="title class_">IUser</span>&gt;(<span class="string">&#x27;user&#x27;</span>, &#123;&#125;)</span><br></pre></td></tr></table></figure>


<h2 id="组件管理"><a href="#组件管理" class="headerlink" title="组件管理"></a>组件管理</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, &#123; memo, useMemo &#125; <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span></span><br><span class="line"><span class="keyword">import</span> &#123; userStorage &#125; <span class="keyword">from</span> <span class="string">&#x27;@/common/storage&#x27;</span></span><br><span class="line">interface <span class="title class_">ITitleProps</span> &#123;  <span class="comment">// 组件参数：I 类型，Title 语义化，Props 类型</span></span><br><span class="line">    <span class="attr">title</span>: string</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">onChange</span> = (<span class="params"></span>) =&gt; &#123;    <span class="comment">// 事件：on 开头</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">Title</span>: <span class="title class_">React</span>.<span class="property">FC</span>&lt;<span class="title class_">ITitleProps</span>&gt; = <span class="function"><span class="params">props</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; title &#125; = props</span><br><span class="line">    <span class="title function_">useEffect</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        userStorage.<span class="title function_">setItem</span>(&#123; <span class="attr">name</span>: <span class="string">&#x27;姓名&#x27;</span>, <span class="attr">age</span>: <span class="number">18</span> &#125;)</span><br><span class="line">        <span class="keyword">const</span> user = userStorage.<span class="title function_">getItem</span>()</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(user) <span class="comment">// &#123; name: &#x27;姓名&#x27;, age: 18 &#125;</span></span><br><span class="line">    &#125;, [])</span><br><span class="line">    <span class="keyword">return</span> (<span class="language-xml"><span class="tag">&lt;<span class="name">h2</span>&gt;</span>&#123;title&#125;<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">memo</span>(<span class="title class_">Title</span>)</span><br></pre></td></tr></table></figure>


<h2 id="接口封装"><a href="#接口封装" class="headerlink" title="接口封装"></a>接口封装</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @/utils/request：基于 axios 二次封装</span></span><br><span class="line"><span class="keyword">import</span> axios, &#123; <span class="title class_">AxiosInstance</span>, <span class="title class_">AxiosRequestConfig</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;axios&#x27;</span></span><br><span class="line"><span class="keyword">import</span> &#123; tokenStorage &#125; <span class="keyword">from</span> <span class="string">&#x27;@/common/storage&#x27;</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> createAxiosIntance = (<span class="attr">baseURL</span>: string): <span class="function"><span class="params">AxiosInstance</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> request = axios.<span class="title function_">create</span>(&#123; baseURL &#125;)</span><br><span class="line">    request.<span class="property">interceptors</span>.<span class="property">request</span>.<span class="title function_">use</span>(<span class="function">(<span class="params">config: AxiosRequestConfig</span>) =&gt;</span> &#123;</span><br><span class="line">        config.<span class="property">headers</span>[<span class="string">&#x27;Authorization&#x27;</span>] = tokenStorage.<span class="title function_">getItem</span>()</span><br><span class="line">        <span class="keyword">return</span> config</span><br><span class="line">    &#125;)</span><br><span class="line">    request.<span class="property">interceptors</span>.<span class="property">response</span>.<span class="title function_">use</span>(</span><br><span class="line">        <span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">const</span> code = res.<span class="property">data</span>.<span class="property">code</span></span><br><span class="line">            <span class="keyword">switch</span> (code) &#123;</span><br><span class="line">              <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line">                  <span class="keyword">return</span> res.<span class="property">data</span></span><br><span class="line">              <span class="keyword">case</span> <span class="number">401</span>: <span class="comment">// 登录失效逻辑</span></span><br><span class="line">                  <span class="keyword">return</span> res.<span class="property">data</span> || &#123;&#125;</span><br><span class="line">              <span class="attr">default</span>:</span><br><span class="line">                  <span class="keyword">return</span> res.<span class="property">data</span> || &#123;&#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="function"><span class="params">error</span> =&gt;</span> &#123;   <span class="comment">// 请求接口报错时返回对象就不用加 try/catch</span></span><br><span class="line">            <span class="keyword">return</span> &#123; <span class="attr">message</span>: <span class="title function_">onErrorReason</span>(error.<span class="property">message</span>) &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    )</span><br><span class="line">    <span class="keyword">return</span> request</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">onErrorReason</span>(<span class="params">message: string</span>): string &#123;</span><br><span class="line">    <span class="keyword">if</span> (message.<span class="title function_">includes</span>(<span class="string">&#x27;Network Error&#x27;</span>)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;网络异常，请检查网络情况!&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (message.<span class="title function_">includes</span>(<span class="string">&#x27;timeout&#x27;</span>)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;请求超时，请重试!&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;服务异常,请重试!&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 登录接口定义</span></span><br><span class="line"><span class="keyword">import</span> &#123; request &#125; <span class="keyword">from</span> <span class="string">&#x27;@/utils/request&#x27;</span></span><br><span class="line"><span class="keyword">export</span> interface <span class="title class_">ILoginParams</span> &#123;   <span class="comment">// 参数</span></span><br><span class="line">    <span class="attr">username</span>: string</span><br><span class="line">    <span class="attr">password</span>: string</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> interface <span class="title class_">ILoginData</span> &#123;     <span class="comment">// 响应</span></span><br><span class="line">    <span class="attr">code</span>: number</span><br><span class="line">    <span class="attr">message</span>: string</span><br><span class="line">    <span class="attr">data</span>: T</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">loginApi</span> = (<span class="params">params: ILoginParams</span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> request.<span class="property">post</span>&lt;<span class="title class_">ILoginData</span>&gt;(<span class="string">&#x27;/login&#x27;</span>, params)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h2 id="状态管理"><a href="#状态管理" class="headerlink" title="状态管理"></a>状态管理</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br></pre></td><td class="code"><pre><span class="line">store    </span><br><span class="line">└─modules           <span class="comment">//  不同模块状态</span></span><br><span class="line">└─user.<span class="property">ts</span>           <span class="comment">//  用户信息状态</span></span><br><span class="line">├─other.<span class="property">ts</span>          <span class="comment">//  其他全局状态</span></span><br><span class="line">├─createStore.<span class="property">ts</span>    <span class="comment">//  封装的管理器</span></span><br><span class="line">└─index.<span class="property">ts</span>          <span class="comment">//  store 入口</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// createStore.ts</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, &#123; </span><br><span class="line">    createContext, </span><br><span class="line">    useContext, </span><br><span class="line">    <span class="title class_">ComponentType</span>, </span><br><span class="line">    <span class="title class_">ComponentProps</span> </span><br><span class="line">&#125; <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span></span><br><span class="line"><span class="keyword">function</span> createStore&lt;T&gt;(<span class="attr">store</span>: <span class="function">() =&gt;</span> T) &#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="title class_">ModelContext</span>: any = &#123;&#125;;</span><br><span class="line">    <span class="keyword">let</span> <span class="attr">currentStore</span>: T;  <span class="comment">// 当前状态</span></span><br><span class="line">    <span class="keyword">let</span> <span class="attr">prevStore</span>: T;     <span class="comment">// 上次状态</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">function</span> useModel&lt;K <span class="keyword">extends</span> keyof T&gt;(<span class="attr">key</span>: K) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">useContext</span>(<span class="title class_">ModelContext</span>[key]) <span class="keyword">as</span> T[K];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建状态注入组件</span></span><br><span class="line">    <span class="keyword">function</span> <span class="title function_">StoreProvider</span>(<span class="params">props: &#123; children: React.ReactNode &#125;</span>) &#123;</span><br><span class="line">        currentStore = <span class="title function_">store</span>();</span><br><span class="line">        <span class="keyword">if</span> (prevStore) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> prevStore) &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="title function_">shallow</span>(prevStore[key], currentStore[key])) &#123;</span><br><span class="line">                    currentStore[key] = prevStore[key];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        prevStore = currentStore;</span><br><span class="line">        <span class="keyword">let</span> <span class="attr">keys</span>: any[] = <span class="title class_">Object</span>.<span class="title function_">keys</span>(currentStore);</span><br><span class="line">        <span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">const</span> length = keys.<span class="property">length</span>;</span><br><span class="line">        <span class="comment">// 遍历状态，递归形成多层级嵌套 Context</span></span><br><span class="line">        <span class="keyword">function</span> getContext&lt;T, K <span class="keyword">extends</span> keyof T&gt;(</span><br><span class="line">            <span class="attr">key</span>: K,</span><br><span class="line">            <span class="attr">val</span>: T,</span><br><span class="line">            <span class="attr">children</span>: <span class="title class_">React</span>.<span class="property">ReactNode</span>,</span><br><span class="line">          ): <span class="variable constant_">JSX</span>.<span class="property">Element</span> &#123;</span><br><span class="line">            <span class="keyword">const</span> <span class="title class_">Context</span> = <span class="title class_">ModelContext</span>[key] || </span><br><span class="line">            (<span class="title class_">ModelContext</span>[key] = <span class="title function_">createContext</span>(val[key]));</span><br><span class="line">            <span class="keyword">const</span> currentIndex = ++i;</span><br><span class="line">            <span class="keyword">let</span> context = <span class="title function_">getContext</span>(keys[currentIndex], val, children)</span><br><span class="line">            <span class="comment">// 返回嵌套的 Context</span></span><br><span class="line">            <span class="keyword">return</span> <span class="title class_">React</span>.<span class="title function_">createElement</span>(</span><br><span class="line">                <span class="title class_">Context</span>.<span class="property">Provider</span>,</span><br><span class="line">                &#123; <span class="attr">value</span>: val[key] &#125;,</span><br><span class="line">                currentIndex &lt; length ? context : children,</span><br><span class="line">            );</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">getContext</span>(keys[i], currentStore, props.<span class="property">children</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取当前状态，方便在组件外部使用，不会引起页面更新</span></span><br><span class="line">    <span class="keyword">function</span> getModel&lt;K <span class="keyword">extends</span> keyof T&gt;(<span class="attr">key</span>: K): T[K] &#123;</span><br><span class="line">        <span class="keyword">return</span> currentStore[key];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 连接 Model 注入到组件</span></span><br><span class="line">    <span class="keyword">function</span> connectModel&lt;<span class="title class_">Selected</span>, K <span class="keyword">extends</span> keyof T&gt;(</span><br><span class="line">        <span class="attr">key</span>: K,</span><br><span class="line">        <span class="attr">selector</span>: <span class="function">(<span class="params">state: T[K]</span>) =&gt;</span> <span class="title class_">Selected</span>,</span><br><span class="line">    ) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">function</span> &lt;P, C <span class="keyword">extends</span> <span class="title class_">ComponentType</span>&lt;any&gt;&gt;(</span><br><span class="line">            <span class="title class_">WarpComponent</span>: C</span><br><span class="line">        ): <span class="title class_">ComponentType</span>&lt;<span class="title class_">Omit</span>&lt;<span class="title class_">ComponentProps</span>&lt;C&gt;, keyof <span class="title class_">Selected</span>&gt;&gt; &#123;</span><br><span class="line">            <span class="keyword">const</span> <span class="title function_">Connect</span> = (<span class="params">props: P</span>) =&gt; &#123;</span><br><span class="line">                <span class="keyword">const</span> val = <span class="title function_">useModel</span>(key);</span><br><span class="line">                <span class="keyword">const</span> state = <span class="title function_">selector</span>(val);</span><br><span class="line">                <span class="keyword">return</span> <span class="title class_">React</span>.<span class="title function_">createElement</span>(<span class="title class_">WarpComponent</span>, &#123;</span><br><span class="line">                    ...props,</span><br><span class="line">                    ...state</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;;</span><br><span class="line">            <span class="keyword">return</span> <span class="title class_">Connect</span> <span class="keyword">as</span> unknown <span class="keyword">as</span> <span class="title class_">ComponentType</span>&lt;</span><br><span class="line">                <span class="title class_">Omit</span>&lt;<span class="title class_">ComponentProps</span>&lt;C&gt;, keyof <span class="title class_">Selected</span>&gt;</span><br><span class="line">            &gt;;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> &#123; useModel, connectModel, <span class="title class_">StoreProvider</span>, getModel &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> createStore</span><br><span class="line"><span class="keyword">function</span> <span class="title class_">Shallow</span>&lt;T&gt;(<span class="attr">obj1</span>: T, <span class="attr">obj2</span>: T) &#123;     <span class="comment">// 对象浅对比</span></span><br><span class="line">    <span class="keyword">if</span> (obj1 === obj2) <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="title class_">Object</span>.<span class="title function_">keys</span>(obj1).<span class="property">length</span> !== <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj2).<span class="property">length</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">in</span> obj1) &#123;</span><br><span class="line">        <span class="keyword">if</span> (obj1[key] !== obj2[key]) <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// store/index.ts</span></span><br><span class="line"><span class="keyword">import</span> &#123; useState &#125; <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span></span><br><span class="line"><span class="keyword">import</span> createStore <span class="keyword">from</span> <span class="string">&#x27;./createStore&#x27;</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">userModel</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">let</span> userData = useState&lt;&#123; <span class="attr">name</span>: string &#125;&gt;(&#123; <span class="attr">name</span>: <span class="string">&#x27;name&#x27;</span> &#125;)</span><br><span class="line">    <span class="keyword">const</span> [ userInfo, setUserInfo ] = userData</span><br><span class="line">    <span class="keyword">return</span> &#123; userInfo, setUserInfo &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">otherModel</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> [ other, setOther ] = useState&lt;number&gt;(<span class="number">20</span>)</span><br><span class="line">    <span class="keyword">return</span> &#123; other, setOther &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> store = <span class="title function_">createStore</span>(<span class="function">() =&gt;</span> (&#123;   <span class="comment">// 组合所有状态</span></span><br><span class="line">    <span class="attr">user</span>: <span class="title function_">userModel</span>(),</span><br><span class="line">    <span class="attr">other</span>: <span class="title function_">otherModel</span>(),</span><br><span class="line">&#125;))</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> &#123; </span><br><span class="line">    useModel, </span><br><span class="line">    <span class="title class_">StoreProvider</span>, </span><br><span class="line">    getModel, </span><br><span class="line">    connectModel </span><br><span class="line">&#125; = store</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// src/main.ts</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">ReactDOM</span> <span class="keyword">from</span> <span class="string">&#x27;react-dom&#x27;</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">App</span> <span class="keyword">from</span> <span class="string">&#x27;@/App&#x27;</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">StoreProvider</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@/store&#x27;</span></span><br><span class="line"><span class="title class_">ReactDOM</span>.<span class="title function_">render</span>(</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">StoreProvider</span>&gt;</span> <span class="tag">&lt;<span class="name">App</span> /&gt;</span> <span class="tag">&lt;/<span class="name">StoreProvider</span>&gt;</span></span>,</span><br><span class="line">    <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;root&#x27;</span>)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>

  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 函数组件中使用，借助 useModel</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span></span><br><span class="line"><span class="keyword">import</span> &#123; useModel &#125; <span class="keyword">from</span> <span class="string">&#x27;@/store&#x27;</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">FunctionDemo</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; userInfo, setUserInfo &#125; = <span class="title function_">useModel</span>(<span class="string">&#x27;user&#x27;</span>)</span><br><span class="line">    <span class="keyword">const</span> <span class="title function_">onChange</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">        <span class="title function_">setUserInfo</span>(&#123; <span class="attr">name</span>: userInfo.<span class="property">name</span> + <span class="string">&#x27;1&#x27;</span> &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (<span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;onChange&#125;</span>&gt;</span>&#123;userInfo.name&#125;--改变<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">FunctionDemo</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// class 组件中使用，借助 connectModel</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, &#123; <span class="title class_">Component</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span></span><br><span class="line"><span class="keyword">import</span> &#123; connectModel &#125; <span class="keyword">from</span> <span class="string">&#x27;@/store&#x27;</span></span><br><span class="line">interface <span class="title class_">IClassDemoProps</span> &#123;</span><br><span class="line">    <span class="attr">setOther</span>: <span class="title class_">React</span>.<span class="property">Dispatch</span>&lt;<span class="title class_">React</span>.<span class="property">SetStateAction</span>&lt;string&gt;&gt;</span><br><span class="line">    <span class="attr">other</span>: number</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ClassDemo</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Component</span>&lt;<span class="title class_">IClassDemoProps</span>&gt; &#123;</span><br><span class="line">    onChange = <span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">props</span>.<span class="title function_">setOther</span>(<span class="variable language_">this</span>.<span class="property">props</span>.<span class="property">other</span> + <span class="number">1</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">render</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;this.onChange&#125;</span>&gt;</span>&#123;this.props.other&#125;<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">connectModel</span>(<span class="string">&#x27;other&#x27;</span>, <span class="function"><span class="params">state</span> =&gt;</span> (&#123;</span><br><span class="line">    <span class="attr">other</span>: state.<span class="property">other</span>,</span><br><span class="line">    <span class="attr">setOther</span>: state.<span class="property">setOther</span></span><br><span class="line">&#125;))(<span class="title class_">ClassDemo</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 组件外使用，借助 getModel</span></span><br><span class="line"><span class="keyword">import</span> &#123; getModel &#125; <span class="keyword">from</span> <span class="string">&#x27;@/store&#x27;</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">onChangeUser</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> user = <span class="title function_">getModel</span>(<span class="string">&#x27;user&#x27;</span>)</span><br><span class="line">    user.<span class="title function_">setUserInfo</span>(&#123; <span class="attr">name</span>: user.<span class="property">userInfo</span>.<span class="property">name</span> + <span class="string">&#x27;1&#x27;</span> &#125;)</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">setTimeout</span>(onChangeUser, <span class="number">1000</span>)</span><br></pre></td></tr></table></figure>


<h2 id="代码规范"><a href="#代码规范" class="headerlink" title="代码规范"></a>代码规范</h2><blockquote>
<p><code>EditorConfig</code> 统一编辑器的代码风格 (编写代码前起作用)、<code>Prettier</code> 保存时格式化代码而统一代码风格 (保存文件时起作用)，<code>ESlint / StyleLint</code> 代码静态分析工具实时检查 js&#x2F;css 并提供错误提示和自动修复 (编写代码时起作用)。具体使用时可体验：editorConfig、prettiter 空格分别设置为 4、2，则代码编辑时回车为 4 格，保存后变成 2 格。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@title</span> 4、代码规范</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> vscode：统一前端编辑器。</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> EditorConfig: 统一团队 vscode 配置。</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> Prettier: 保存文件时自动格式化代码。</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> Eslint: 实时检测 js 的语法规范和错误。</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> Stylelint: 检测和格式化 css 语法。</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// .editorconfig：编辑器配置文件</span></span><br><span class="line"> root = <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> [*]</span><br><span class="line"> charset = utf-<span class="number">8</span></span><br><span class="line"> indent_style = space</span><br><span class="line"> indent_size = <span class="number">2</span>                     # 缩紧大小</span><br><span class="line"> charset = utf-<span class="number">8</span>                     # 编码格式</span><br><span class="line"> end_of_line = lf                    # 换行符格式</span><br><span class="line"> insert_final_newline = <span class="literal">true</span>         # 是否在文件最后插入一个空行</span><br><span class="line"> trim_trailing_whitespace = <span class="literal">true</span>     # 是否删除行尾的空格</span><br><span class="line"></span><br><span class="line"> [*.<span class="property">md</span>]</span><br><span class="line"> max_line_length = <span class="number">0</span>                 # 强制在指定字符数之后换行</span><br><span class="line"> trim_trailing_whitespace = <span class="literal">false</span>    # md 会用尾空格表示换行，不自动移除</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// .prettierrc：npm install prettier -D</span></span><br><span class="line"> &#123;</span><br><span class="line">   <span class="string">&quot;trailingComma&quot;</span>: <span class="string">&quot;all&quot;</span>,           <span class="comment">// 对象的最后一个属性末尾也添加 ,</span></span><br><span class="line">   <span class="string">&quot;tabWidth&quot;</span>: <span class="number">2</span>,                    <span class="comment">// 缩进大小</span></span><br><span class="line">   <span class="string">&quot;semi&quot;</span>: <span class="literal">false</span>,                    <span class="comment">// 分号是否添加</span></span><br><span class="line">   <span class="string">&quot;singleQuote&quot;</span>: <span class="literal">true</span>,              <span class="comment">// 是否单引号</span></span><br><span class="line">   <span class="string">&quot;jsxSingleQuote&quot;</span>: <span class="literal">true</span>,           <span class="comment">// jsx 语法下是否单引号</span></span><br><span class="line">   <span class="string">&quot;endOfLine&quot;</span>: <span class="string">&quot;lf&quot;</span>,                <span class="comment">// 与 .editorconfig 保持一致设置</span></span><br><span class="line">   <span class="string">&quot;printWidth&quot;</span>: <span class="number">120</span>,                <span class="comment">// 单行代码最长字符长度，超过则换行</span></span><br><span class="line">   <span class="string">&quot;bracketSpacing&quot;</span>: <span class="literal">true</span>,           <span class="comment">// 对象中的括号之间打印空格</span></span><br><span class="line">   <span class="string">&quot;arrowParens&quot;</span>: <span class="string">&quot;always&quot;</span>           <span class="comment">// 箭头函数的参数都用括号包裹</span></span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// .eslintrc.js：npm install eslint eslint-config-prettier -D</span></span><br><span class="line"> <span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">   <span class="attr">env</span>: &#123; <span class="attr">browser</span>: <span class="literal">true</span>, <span class="attr">es2020</span>: <span class="literal">true</span>, <span class="attr">node</span>: <span class="literal">true</span> &#125;,</span><br><span class="line">   <span class="attr">extends</span>: [                     <span class="comment">// 下载插件的预设规则</span></span><br><span class="line">     <span class="string">&#x27;airbnb&#x27;</span></span><br><span class="line">     <span class="string">&#x27;prettier&#x27;</span>,</span><br><span class="line">     <span class="string">&#x27;prettier/@typescript-eslint&#x27;</span>,</span><br><span class="line">     <span class="string">&#x27;prettier/react&#x27;</span>,</span><br><span class="line">     <span class="string">&#x27;prettier/unicorn&#x27;</span>,</span><br><span class="line">     <span class="string">&#x27;plugin:react/recommended&#x27;</span>, </span><br><span class="line">   ],  </span><br><span class="line">   <span class="attr">parser</span>: <span class="string">&#x27;@typescript-eslint/parser&#x27;</span>,</span><br><span class="line">   <span class="attr">parserOptions</span>: &#123;</span><br><span class="line">     <span class="attr">ecmaFeatures</span>: &#123; <span class="attr">jsx</span>: <span class="literal">true</span> &#125;,</span><br><span class="line">     <span class="attr">ecmaVersion</span>: <span class="number">11</span>,</span><br><span class="line">     <span class="attr">sourceType</span>: <span class="string">&#x27;module&#x27;</span>,</span><br><span class="line">   &#125;,</span><br><span class="line">   <span class="attr">settings</span>: &#123;</span><br><span class="line">     <span class="string">&#x27;import/resolver&#x27;</span>: &#123;     <span class="comment">// .ts、.tsx 文件互相引用时不报错</span></span><br><span class="line">       <span class="attr">node</span>: &#123; <span class="attr">extensions</span>: [<span class="string">&#x27;.tsx&#x27;</span>, <span class="string">&#x27;.ts&#x27;</span>, <span class="string">&#x27;.js&#x27;</span>, <span class="string">&#x27;.json&#x27;</span>] &#125;</span><br><span class="line">     &#125;</span><br><span class="line">   &#125;,</span><br><span class="line">   <span class="attr">plugins</span>: [<span class="string">&#x27;react&#x27;</span>, <span class="string">&#x27;@typescript-eslint&#x27;</span>],   <span class="comment">// npm 安装后才能使用的插件</span></span><br><span class="line">   <span class="attr">rules</span>: &#123;&#125;,</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// .stylelintrc.js</span></span><br><span class="line"> <span class="comment">// npm i stylelint stylelint-config-standard stylelint-config-prettier -D</span></span><br><span class="line"> <span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">   <span class="attr">extends</span>: [<span class="string">&#x27;stylelint-config-standard&#x27;</span>, <span class="string">&#x27;stylelint-config-prettier&#x27;</span>],</span><br><span class="line">   <span class="attr">rules</span>: &#123;</span><br><span class="line">     <span class="string">&#x27;comment-empty-line-before&#x27;</span>: <span class="literal">null</span>,</span><br><span class="line">     <span class="string">&#x27;declaration-empty-line-before&#x27;</span>: <span class="literal">null</span>,</span><br><span class="line">     <span class="string">&#x27;function-name-case&#x27;</span>: <span class="string">&#x27;lower&#x27;</span>,</span><br><span class="line">     <span class="string">&#x27;no-descending-specificity&#x27;</span>: <span class="literal">null</span>,</span><br><span class="line">     <span class="string">&#x27;no-invalid-double-slash-comments&#x27;</span>: <span class="literal">null</span>,</span><br><span class="line">     <span class="string">&#x27;rule-empty-line-before&#x27;</span>: <span class="string">&#x27;always&#x27;</span>,</span><br><span class="line">   &#125;,</span><br><span class="line">   <span class="attr">ignoreFiles</span>: [<span class="string">&#x27;node_modules/**/*&#x27;</span>, <span class="string">&#x27;build/**/*&#x27;</span>],</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// package.json：新增校验代码的命令</span></span><br><span class="line"><span class="attr">scripts</span>: &#123;</span><br><span class="line">   <span class="string">&quot;lint&quot;</span>: <span class="string">&quot;npm run lint-eslint &amp;&amp; npm run lint-stylelint&quot;</span>,</span><br><span class="line">   <span class="string">&quot;lint-eslint&quot;</span>: <span class="string">&quot;eslint -c .eslintrc.js --ext .ts,.tsx,.js src&quot;</span>,</span><br><span class="line">   <span class="string">&quot;lint-stylelint&quot;</span>: <span class="string">&quot;stylelint --config .stylelintrc.js src/**/*.&#123;css,scss&#125;&quot;</span></span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>


<h2 id="样式命名"><a href="#样式命名" class="headerlink" title="样式命名"></a>样式命名</h2><ul>
<li>属性顺序<ul>
<li>位置：position、top、z-index、display、float</li>
<li>大小：width、height、padding、margin</li>
<li>文字：font、color、line-height、text-align</li>
<li>背景：background、border</li>
<li>其他：animation、transition</li>
</ul>
</li>
<li>类名规范<ul>
<li>尽量使用语义化、小写的英文单词。</li>
<li>长名称或词组可使用中横线，不可使用下划线。</li>
<li>大型项目统一类名方案 <code>BEM：块 block - 元素 element -- 修饰 modifier</code>。</li>
</ul>
</li>
</ul>
  <figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// BEM 命名约定</span></span><br><span class="line"><span class="selector-class">.block</span> &#123; &#125;              <span class="comment">// 模块类名</span></span><br><span class="line"><span class="selector-class">.block</span>**element &#123; &#125;     <span class="comment">// 模块中的元素类名</span></span><br><span class="line"><span class="selector-class">.block--modifier</span> &#123; &#125;    <span class="comment">// 不同状态、版本的模块类名</span></span><br><span class="line"></span><br><span class="line"><span class="selector-class">.c-row-age</span> &#123; &#125;          <span class="comment">// components/RowAge/index.js</span></span><br><span class="line"><span class="selector-class">.cc-addable-block</span> &#123; &#125;   <span class="comment">// components/common/AddableBlock/index.js</span></span><br><span class="line"><span class="selector-class">.cc-addable-block--disabled</span> &#123; &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Element UI 命名</span></span><br><span class="line"><span class="selector-class">.el-input</span>,</span><br><span class="line"><span class="selector-class">.el-input</span>**inner &#123;  </span><br><span class="line">    <span class="attribute">cursor</span>: pointer;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.el-input</span><span class="selector-class">.is-focus</span> <span class="selector-class">.el-input</span>**inner &#123;  </span><br><span class="line">    <span class="attribute">border-color</span>: <span class="variable">$--input-focus-border</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.el-input</span>**<span class="attribute">icon</span> &#123;</span><br><span class="line">    <span class="attribute">transition</span>: none;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.el-pagination--small</span> &#123;  </span><br><span class="line">    <span class="selector-class">.btn-prev</span>,  </span><br><span class="line">    <span class="selector-class">.btn-next</span>,  </span><br><span class="line">    <span class="selector-class">.el-pager</span> <span class="selector-tag">li</span> &#123;    </span><br><span class="line">        <span class="attribute">margin</span>: <span class="number">0</span> <span class="number">3px</span>;   </span><br><span class="line">        <span class="attribute">min-width</span>: <span class="number">22px</span>;  </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h2 id="路由模式"><a href="#路由模式" class="headerlink" title="路由模式"></a>路由模式</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> hash 模式：地址后加 #value，更新时不会向浏览器发送请求</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 修改：&lt;a href=&quot;#1&quot;&gt;显示1&lt;/a&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 监听：window.addEventListener(&#x27;hashchange&#x27;, fn&#125;)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 获取：window.location.hash</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">let</span> page1 = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;#page1&#x27;</span>);</span><br><span class="line"><span class="keyword">let</span> page2 = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;#page2&#x27;</span>);</span><br><span class="line"><span class="keyword">let</span> page3 = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;#page3&#x27;</span>);</span><br><span class="line"><span class="keyword">let</span> page4 = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;#page4&#x27;</span>);</span><br><span class="line"><span class="comment">// hash 表</span></span><br><span class="line"><span class="keyword">const</span> routeTable = &#123; <span class="number">1</span>: page1, <span class="number">2</span>: page2, <span class="number">3</span>: page3, <span class="number">4</span>: page4 &#125;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">route</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> number = <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">hash</span>.<span class="title function_">slice</span>(<span class="number">1</span>);</span><br><span class="line">    number = number || <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">let</span> page = routeTable[number.<span class="title function_">toString</span>()];</span><br><span class="line">    page = page || <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;#page404&#x27;</span>);</span><br><span class="line">    page.<span class="property">style</span>.<span class="property">display</span> = <span class="string">&#x27;block&#x27;</span>;</span><br><span class="line">    <span class="keyword">let</span> app = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;#app&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span> (app.<span class="property">children</span>.<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        app.<span class="property">innerHTML</span> = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    app.<span class="title function_">appendChild</span>(page);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 初始渲染和监听变化</span></span><br><span class="line"><span class="title function_">route</span>();</span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;hashchange&#x27;</span>,<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="title function_">route</span>();</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> history 模式：修改 URL 但不发送请求的方法主要如下</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> back()：后退到上一个路由。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> forward()：前进到下一个路由。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> go(number)：进入到其它路由，正数前进，负数后退。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> pushState(obj, title, url)：前进到指定路由，不刷新页面。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> replaceState(obj, title, url)：替换当前路由，不刷新页面。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line">history.<span class="title function_">pushState</span>(state 数据, title 标题, url 新地址);</span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;popstate&#x27;</span>, <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;history.pushState 触发&#x27;</span>, location.<span class="property">pathname</span>);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h2 id="框架选择"><a href="#框架选择" class="headerlink" title="框架选择"></a>框架选择</h2><ul>
<li>框架和库<ul>
<li><strong>类库</strong>：一个有组织的功能集合，用于提供特定功能的接口并被调用。</li>
<li><strong>框架</strong>：构建应用程序的整体架构，使开发者可以专注于逻辑处理而提高开发效率。。</li>
<li>联系：框架一般会集成大量库并在合适地方调用，有时称为库，区别在于思考角度。常用库 Echarts&#x2F;ElementUI、常用框架 Vue&#x2F;React&#x2F;AngularJS。</li>
</ul>
</li>
<li>框架发展<ul>
<li><strong>完整应用程序的 MV 框架 Angular</strong>：Node&#x2F;Npm 尝试编译 JS 代码，AMD&#x2F;CommonJS 定义 JS 模块，Gulp&#x2F;Grunt 等构建工具将模块组合成一个应用，路由和模板等语法则降低了开发难度并提高了和用户体验。</li>
<li><strong>基于组件的 Vue、React</strong>：基于组件的应用程序消除了很多抽象概念并简化了代码的生命周期，一切都与组件而不是应用程序的生命周期联系在一起，这意味着框架可以专注于解决渲染问题，路由、通信和状态管理则交给开发人员自己配置。这个时代 JS 生态系统的发展主要有 SPA 单页面、SSR 渲染优化、Babel 转译器、ES 模块化、API 标准化，但是也存在状态管理、响应性、SPA 性能等问题。</li>
<li><strong>融合前后端的全栈式框架</strong>：Next.js (React)、Nuxt.js (Vue)、Remix (React)、SvelteKit (Svelte)、Gatsby (React)、Astro (Any)，这些元框架随着视图层的成熟而开始标准化路由器、构建系统等。随着服务器端和客户端的 JS 标准越来越接近，前后端代码也可能实现同构而解决渲染性能、请求代理等问题。</li>
</ul>
</li>
</ul>
<h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2>  <div align="center"> 
    <img src="" data-original="/images/mobile/optimization.png" alt="移动端优化" width="100%">
  </div>


<h1 id="六、网站安全规范"><a href="#六、网站安全规范" class="headerlink" title="六、网站安全规范"></a>六、网站安全规范</h1><ul>
<li>该用验证码的时候一定要添上。</li>
<li>谨慎用户输入信息，进行输入检查（客户端和服务端同时检查）。</li>
<li>在变量输出到 HTML 页面时，都应该进行编码或转义来预防XSS攻击。</li>
<li>尽量在重要请求上添加 Token 参数，注意 Token 要用足够安全的随机数生成算法。</li>
</ul>
<h2 id="XSS-跨站脚本攻击"><a href="#XSS-跨站脚本攻击" class="headerlink" title="XSS 跨站脚本攻击"></a>XSS 跨站脚本攻击</h2><blockquote>
<p>指黑客通过 “HTML 注入” 篡改了网页，插入了恶意的脚本（主要是 JS 脚本），从而在用户浏览网页时控制用户浏览器的一种攻击。常见的有 挂马、盗取用户 Cookie、钓鱼攻击、删除目标文章或恶意篡改数据、劫持用户 Web 行为甚至渗透内网、爆发 Web2.0 蠕虫、蠕虫式挂马攻击(刷广告&#x2F;刷浏量&#x2F;破坏网上数据)。</p>
</blockquote>
<ul>
<li><strong>常见类型</strong><ul>
<li><strong>反射型 XSS</strong>：即 HTML 注入、非持久型 XSS。把用户输入的数据反射给浏览器，即黑客往往需要诱使用户点击一个恶意链接后将用户输入的隐私数据拼接出 html 并执行恶意 js 脚本，这个过程像是<strong>一次反射</strong>。</li>
<li><strong>存储型 XSS</strong>：持久型 XSS，它会将用户输入的数据存储在攻击方的服务器，具有很强的<strong>稳定性</strong>，比如黑客写了一篇包含恶意 JS 脚本的博客文章并保存到服务端。</li>
<li><strong>DOM based XSS</strong>：效果同反射型 XSS，但是它是通过修改页面的<strong>DOM 节点</strong>形成的。比如通过修改 DOM 绑定方法来获取用户信息。</li>
</ul>
</li>
<li><strong>检测 XSS</strong><ul>
<li><strong>检测方式</strong>：用户可以在有关键字输入搜索的地方输入********后点击搜索，若弹框出现展示123，说明存在XSS漏洞，这说明前端并没有对用户输入的内容过滤处理。</li>
</ul>
</li>
<li><strong>XSS 攻击方式</strong><ul>
<li><strong>Cookie 劫持</strong>：通过伪装一些图片和按钮等诱使用户对其操作，使网页执行了攻击者的恶意脚本，使攻击者能够获取当前用户的Cookie信息。</li>
<li><strong>构造 GET&#x2F;POST 请求</strong>：若攻击者想删除某网站的一篇文章，首先获取文章 id，然后通过脚本插入图片发送一个 GET&#x2F;POST 请求。</li>
<li><strong>XSS 钓鱼</strong>：在对方不知情的情况下诱骗用户并得到对方的姓名、年龄、邮箱账号、甚至是银行卡密码等私人信息。比如某用户在已被攻击的某网站上操作黑客伪造的一个登录框输入个人信息，用户信息就会被上传到黑客的服务器上。</li>
<li><strong>获取用户真实 IP</strong>：通过第三方软件获取，比如客户端安装了 Java 环境 (JRE)，则可通过调用<code>Java Applet</code>的接口获取客户端本地的 IP 地址。</li>
</ul>
</li>
<li><strong>XSS 防御方式</strong><ul>
<li><strong>HttpOnly</strong>：浏览器禁止页面 JS 访问带有 HttpOnly 属性的 cookie，可解决 cookie 劫持问题。<code>response.setHeader(&quot;Set-Cookie&quot;,&quot;cookiename=value; Path=/;Domain=domainvalue;Max-Age=seconds;HTTPOnly&quot;)</code>。</li>
<li><strong>输入检查</strong>：检查是否包含 JavaScript、script 等敏感字符并将特殊字符转义处理，让一些基于特殊字符的攻击失效。现在的普遍做法是前后端都做输入检查，因为客户端的检查很容易被攻击者绕过。</li>
<li><strong>输出检查</strong>：在变量输出到 HTML 页面时，除了富文本外都使用编码或转义的方式。</li>
<li><strong>更严格的做法</strong>：除了数字和字母外的所有字符，都使用十六进制的方式进行编码。</li>
</ul>
</li>
</ul>
<h2 id="CSRF-跨站点请求伪造"><a href="#CSRF-跨站点请求伪造" class="headerlink" title="CSRF 跨站点请求伪造"></a>CSRF 跨站点请求伪造</h2><blockquote>
<p>指利用用户身份操作用户账户的一种攻击方式，即攻击者诱使用户访问一个第三方有害页面执行操作并获取用户信息，然后攻击者使用这个伪造的真实身份到某网站执行恶意操作。但是攻击者只有预测到 URL 的所有参数和参数值，才能成功地伪造一个请求 (他可以在安全站点里以自己的身份实际操作一下拿到参数)，反之则无法攻击成功。</p>
</blockquote>
<ul>
<li><strong>攻击方式</strong><ul>
<li><strong>浏览器 Cookie</strong>：Session Cookie (关闭失效)、Third-party Cookie (过期失效)。</li>
<li><strong>P3P Header 副作用</strong>：P3P Header 是 W3C 制定的隐私偏好平台，如果网站返回给浏览器的 HTTP 头包含，则表示允许浏览器发送第三方 Cookie，主要应用在广告等需要跨域访问的页面。</li>
<li><strong>GET&#x2F;POST 请求</strong>：通过 img&#x2F;iframe&#x2F;script 等带有 src 的标签发送 GET 请求，通过一个用户不可见的 iframe 窗口构造一个 form 表单发送 POST 请求。</li>
</ul>
</li>
<li><strong>防御方式</strong><ul>
<li><strong>验证码</strong>：CSRF 攻击过程中会在用户在不知情的情况下构造了网络请求，添加验证码后则强制用户必须与应用进行交互。缺点是网站不能给所有的操作都加上验证码。</li>
<li><strong>Referer Check</strong>：添加一个拦截器通过检查 HTTP 头中的 Referer 来判断请求来源是否合法。缺点是很多网站为了保护用户隐私而限制了 Referer 的发送、浏览器从 HTTPS 跳转到 HTTP 时出于安全考虑而不会发送 Referer。</li>
<li><strong>Anti CSRF Token</strong>：将参数加密或使用 Token 随机数，从而让攻击者无法猜测到参数值，也就无法构造请求的 URL 从而发起 CSRF 攻击。比如新增参数 Token <code>http://host/path/delete?username=abc&amp;item=123&amp;token=[random(seed)]</code>。优点是比检查 Referer 更安全并且不涉及用户隐私，缺点是 token 时需要所有请求都添加，加密时则使 URL 可读性变差、加密参数导致用户无法对页面进行搜索并给 DBA 工作带来困扰 (数据分析常需要用到参数的明文)。</li>
<li><strong>XSRF</strong>：当网站同时存在 XSS、CSRF 漏洞时，攻击者可以模拟客户端执行任意操作并在请求页面后读取页面内容的 Token 值，然后再构造出一个合法请求。</li>
</ul>
</li>
</ul>
<h2 id="ClickJacking-点击劫持"><a href="#ClickJacking-点击劫持" class="headerlink" title="ClickJacking 点击劫持"></a>ClickJacking 点击劫持</h2><blockquote>
<p>一种视觉上的欺骗手段。攻击者使用一个透明的、不可见的 iframe 覆盖在一个网页上，然后诱使不知情的用户在网页上进行点击。比如在访问某网页时点击空白区域，浏览器却意外打开了新葡京赌场的页面。防御方式如下：</p>
</blockquote>
<ul>
<li><strong>_X-Frame-Options HTTP 响应头</strong>：用来给浏览器指示一个页面能否在 frame&#x2F;iframe&#x2F;object 标签中展现的标记。它有三个可选的值：DENY (拒绝加载)、SAMEORIGIN (允许加载同源页面)、ALLOW-FROM (允许加载指定来源的页面)。</li>
<li><strong>禁止iframe的嵌套</strong>：<code>if(window.top.location !== window.loaction)&#123;window.top.location === window.self.location&#125;</code>。</li>
</ul>
<h2 id="其他安全问题"><a href="#其他安全问题" class="headerlink" title="其他安全问题"></a>其他安全问题</h2><ul>
<li><strong>跨域问题处理</strong>：当服务端设置 ‘Access-Control-Allow-Origin’ 时使用了通配符 “*”，允许来自任意域的跨域请求，这是极其危险的。</li>
<li><strong>postMessage 跨窗口传递信息</strong>：postMessage 允许每一个 window（包括当前窗口、弹出窗口、iframes等）对象往其它窗口发送文本消息，从而实现跨窗口的消息传递并且不受同源策略限制。必要时在接受窗口对接口的信息进行安全检查 (验证 Domain、URL)，以防止来自非法页面的消息，实际上是实现一次同源策略的验证过程。</li>
<li><strong>Web Storage</strong>：分为 Session Storage、Local Storage。虽然受同源策略的约束，但当存有敏感信息时也可能会成为攻击目标。</li>
</ul>

    </div>

    
    
    <div>
      
        <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px;margin-top:20px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    
</div>

      
    </div>
    
        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>chuangges
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://chuangges.gitee.io/2019/02/17/2-1-web%E5%BC%80%E5%8F%91%E9%A1%BB%E7%9F%A5/" title="web 开发须知">https://chuangges.gitee.io/2019/02/17/2-1-web开发须知/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/zh_CN" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2019/02/09/1-hexo%E5%8D%9A%E5%AE%A2/" rel="prev" title="Hexo + Github 搭建个人博客">
      <i class="fa fa-chevron-left"></i> Hexo + Github 搭建个人博客
    </a></div>
      <div class="post-nav-item">
    <a href="/2019/02/23/2-3-web%E5%B7%A5%E5%85%B7/" rel="next" title="web 开发工具">
      web 开发工具 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E6%8A%80%E6%9C%AF%E7%BD%91%E7%AB%99"><span class="nav-number">1.</span> <span class="nav-text">一、技术网站</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81%E6%9E%B6%E6%9E%84%E6%A8%A1%E5%BC%8F"><span class="nav-number">2.</span> <span class="nav-text">二、架构模式</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#CSR-%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%B8%B2%E6%9F%93"><span class="nav-number">2.1.</span> <span class="nav-text">CSR 客户端渲染</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Next-x2F-Nuxt-%E5%90%8C%E6%9E%84%E6%B8%B2%E6%9F%93"><span class="nav-number">2.2.</span> <span class="nav-text">Next&#x2F;Nuxt 同构渲染</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#SSR-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B8%B2%E6%9F%93"><span class="nav-number">2.3.</span> <span class="nav-text">SSR 服务端渲染</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#SSG-%E9%9D%99%E6%80%81%E9%A2%84%E6%B8%B2%E6%9F%93"><span class="nav-number">2.4.</span> <span class="nav-text">SSG 静态预渲染</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ISR-%E5%A2%9E%E9%87%8F%E9%9D%99%E6%80%81%E6%B8%B2%E6%9F%93"><span class="nav-number">2.5.</span> <span class="nav-text">ISR 增量静态渲染</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E3%80%81%E6%B5%8F%E8%A7%88%E5%99%A8%E6%9C%BA%E5%88%B6"><span class="nav-number">3.</span> <span class="nav-text">三、浏览器机制</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%8A%BD%E8%B1%A1%E5%88%86%E5%B1%82%E7%BB%93%E6%9E%84"><span class="nav-number">3.1.</span> <span class="nav-text">抽象分层结构</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%A4%9A%E8%BF%9B%E7%A8%8B%E6%9C%BA%E5%88%B6"><span class="nav-number">3.2.</span> <span class="nav-text">多进程机制</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6"><span class="nav-number">3.3.</span> <span class="nav-text">渲染机制</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88"><span class="nav-number">3.4.</span> <span class="nav-text">优化方案</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%9B%9B%E3%80%81JS-%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6"><span class="nav-number">4.</span> <span class="nav-text">四、JS 运行机制</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#JS-%E5%BC%95%E6%93%8E"><span class="nav-number">4.1.</span> <span class="nav-text">JS 引擎</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#JS-%E7%BA%BF%E7%A8%8B"><span class="nav-number">4.2.</span> <span class="nav-text">JS 线程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#JS-%E4%BB%BB%E5%8A%A1"><span class="nav-number">4.3.</span> <span class="nav-text">JS 任务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%BC%82%E6%AD%A5%E6%9C%BA%E5%88%B6"><span class="nav-number">4.4.</span> <span class="nav-text">异步机制</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%94%E3%80%81%E9%A1%B9%E7%9B%AE%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83"><span class="nav-number">5.</span> <span class="nav-text">五、项目开发规范</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%A1%B9%E7%9B%AE%E7%9B%AE%E5%BD%95"><span class="nav-number">5.1.</span> <span class="nav-text">项目目录</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%85%AC%E5%85%B1%E6%96%B9%E6%B3%95"><span class="nav-number">5.2.</span> <span class="nav-text">公共方法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BB%84%E4%BB%B6%E7%AE%A1%E7%90%86"><span class="nav-number">5.3.</span> <span class="nav-text">组件管理</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%8E%A5%E5%8F%A3%E5%B0%81%E8%A3%85"><span class="nav-number">5.4.</span> <span class="nav-text">接口封装</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86"><span class="nav-number">5.5.</span> <span class="nav-text">状态管理</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83"><span class="nav-number">5.6.</span> <span class="nav-text">代码规范</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A0%B7%E5%BC%8F%E5%91%BD%E5%90%8D"><span class="nav-number">5.7.</span> <span class="nav-text">样式命名</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%B7%AF%E7%94%B1%E6%A8%A1%E5%BC%8F"><span class="nav-number">5.8.</span> <span class="nav-text">路由模式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A1%86%E6%9E%B6%E9%80%89%E6%8B%A9"><span class="nav-number">5.9.</span> <span class="nav-text">框架选择</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96"><span class="nav-number">5.10.</span> <span class="nav-text">性能优化</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%85%AD%E3%80%81%E7%BD%91%E7%AB%99%E5%AE%89%E5%85%A8%E8%A7%84%E8%8C%83"><span class="nav-number">6.</span> <span class="nav-text">六、网站安全规范</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#XSS-%E8%B7%A8%E7%AB%99%E8%84%9A%E6%9C%AC%E6%94%BB%E5%87%BB"><span class="nav-number">6.1.</span> <span class="nav-text">XSS 跨站脚本攻击</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#CSRF-%E8%B7%A8%E7%AB%99%E7%82%B9%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0"><span class="nav-number">6.2.</span> <span class="nav-text">CSRF 跨站点请求伪造</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ClickJacking-%E7%82%B9%E5%87%BB%E5%8A%AB%E6%8C%81"><span class="nav-number">6.3.</span> <span class="nav-text">ClickJacking 点击劫持</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%85%B6%E4%BB%96%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98"><span class="nav-number">6.4.</span> <span class="nav-text">其他安全问题</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="chuangges" src="" data-original="/images/chuang.png">
  <p class="site-author-name" itemprop="name">chuangges</p>
  <div class="site-description" itemprop="description">chuangge's blog.</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">39</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/chuangges" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;chuangges" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://stackoverflow.com/yourname" title="StackOverflow → https:&#x2F;&#x2F;stackoverflow.com&#x2F;yourname" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-stack-overflow fa-fw"></i>StackOverflow</a>
      </span>
  </div>


  <div class="links-of-blogroll motion-element">
    <div class="links-of-blogroll-title"><i class="fa fa-link fa-fw"></i>
      友情链接
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="https://juejin.im/" title="https:&#x2F;&#x2F;juejin.im&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">掘金</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://www.nowcoder.com/" title="https:&#x2F;&#x2F;www.nowcoder.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">牛客网</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://www.docschina.org/" title="https:&#x2F;&#x2F;www.docschina.org&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">印记中文</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="http://tool.lu/" title="http:&#x2F;&#x2F;tool.lu&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">在线工具</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://tinypng.com/" title="https:&#x2F;&#x2F;tinypng.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">图片压缩</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://bigjpg.com/" title="https:&#x2F;&#x2F;bigjpg.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">图片放大</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://tool.chinaz.com/" title="https:&#x2F;&#x2F;tool.chinaz.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">站长工具</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://learn-anything.xyz/" title="https:&#x2F;&#x2F;learn-anything.xyz&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">Learn-Anything</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://stackoverflow.com/" title="https:&#x2F;&#x2F;stackoverflow.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">Stack-Overflow</a>
        </li>
    </ul>
  </div>

      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2024</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">chuangge</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-area"></i>
    </span>
    <span title="站点总字数">1m</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">15:14</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="external nofollow noopener noreferrer" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="external nofollow noopener noreferrer" target="_blank">NexT.Pisces</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

<script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"pluginRootPath":"live2dw/","pluginJsPath":"lib/","pluginModelPath":"assets/","model":{"jsonPath":"/live2dw/assets/tororo.model.json"},"display":{"position":"left","width":180,"height":360},"mobile":{"show":false},"react":{"opacityDefault":1,"opacityOnHover":1},"log":false,"tagMode":false});</script>
        <style>
            [bg-lazy] {
                background-image: none !important;
                background-color: #eee !important;
            }
        </style>
        <script>
            window.imageLazyLoadSetting = {
                isSPA: false,
                preloadRatio: 1,
                processImages: null,
            };
        </script><script>window.addEventListener("load",function(){var t=/\.(gif|jpg|jpeg|tiff|png)$/i,r=/^data:image\/[a-z]+;base64,/;Array.prototype.slice.call(document.querySelectorAll("img[data-original]")).forEach(function(a){var e=a.parentNode;"A"===e.tagName&&(e.href.match(t)||e.href.match(r))&&(e.href=a.dataset.original)})});</script><script>!function(e){e.imageLazyLoadSetting.processImages=t;var n=e.imageLazyLoadSetting.isSPA,i=e.imageLazyLoadSetting.preloadRatio||1,r=o();function o(){var t=Array.prototype.slice.call(document.querySelectorAll("img[data-original]")),e=Array.prototype.slice.call(document.querySelectorAll("[bg-lazy]"));return t.concat(e)}function t(){n&&(r=o());for(var t,a=0;a<r.length;a++)0<=(t=(t=r[a]).getBoundingClientRect()).bottom&&0<=t.left&&t.top<=(e.innerHeight*i||document.documentElement.clientHeight*i)&&function(){var t,e,n,i,o=r[a];e=function(){r=r.filter(function(t){return o!==t})},(t=o).hasAttribute("bg-lazy")?(t.removeAttribute("bg-lazy"),e&&e()):(n=new Image,i=t.getAttribute("data-original"),n.onload=function(){t.src=i,t.removeAttribute("data-original"),e&&e()},t.src!==i&&(n.src=i))}()}function a(){clearTimeout(t.tId),t.tId=setTimeout(t,500)}t(),document.addEventListener("scroll",a),e.addEventListener("resize",a),e.addEventListener("orientationchange",a)}(this);</script></body>
</html>
